from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist

from django.core.files import File
from django.db.models.signals import post_save, post_delete
from django.db import models, IntegrityError, transaction
from django.template.defaultfilters import slugify
from imagekit.models import ImageModel
from datetime import datetime
    
class NameSlugModel(models.Model):
    name = models.CharField(max_length=255)
    slug = models.SlugField(unique=True, editable=False)

    class Meta:
        abstract = True

    def save(self, *args, **kwargs):
        """
        Based on the Tag save() method in django-taggit, this method simply
        stores a slugified version of the name, ensuring that the unique
        constraint is observed
        """

        self.slug = slug = slugify(self.name)
        i = 0
        while True:
            try:
                savepoint = transaction.savepoint()
                res = super(NameSlugModel, self).save(*args, **kwargs)
                transaction.savepoint_commit(savepoint)
                return res
            except IntegrityError:
                transaction.savepoint_rollback(savepoint)
                i += 1
                self.slug = '%s_%d' % (slug, i)

class Photo(ImageModel):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey()
    
    original_image = models.ImageField(upload_to='photos')
    num_views = models.PositiveIntegerField(editable=False, default=0)
    user = models.ForeignKey(User, null=True)
     
    def admin_image(self):
        if settings.DEBUG:
            return '<img src="%s%s"/>' % (settings.DEBUG_MEDIA_URL, self.thumbnail.url)
        else:
            return '<img src="%s%s />' % (settings.MEDIA_URL, self.thumbnail.url)
    
    admin_image.allow_tags = True
    
    class IKOptions:
        spec_module = 'apps.food.specs'
        image_field = 'original_image'
        save_count_as = 'num_views'

def create_profile_photo(user):
    with open(settings.DEFAULT_PROFILE_PIC_FILENAME, 'r') as f:
        p = ProfilePhoto(content_object=user, original_image=File(f), user=user)
        p.save()
        profile = user.get_profile()
        profile.pic = p
        profile.save()

def create_dish_photo(dish):
    with open(settings.DEFAULT_PROFILE_PIC_FILENAME, 'r') as f:
        p = Photo(content_object=dish, original_image=File(f))
        p.save()
        dish.pic = p
        dish.save()
    
def create_profile_photo_receiver(sender, **kw):
    print "entering create_profile_photo_receiver"
    user = kw["instance"].content_object
    
    if not user.get_profile().pic:
        create_profile_photo(user)

def create_profile_receiver(sender, **kw):
    user = kw["instance"]
    if kw["created"] and not UserProfile.objects.filter(user=user):
        up = UserProfile(user=user)
        up.save()
        
#        p = create_profile_photo(user)
#        up = UserProfile(user=user, pic=p)
#        up.save()

post_save.connect(create_profile_receiver, sender=User)


class ProfilePhoto(Photo):

        
    class IKOptions:
        spec_module = 'apps.food.specs_profile'
        image_field = 'original_image'
        save_count_as = 'num_views'
        
        
post_delete.connect(create_profile_photo_receiver, sender=ProfilePhoto)

class UserProfile(models.Model):

    user = models.OneToOneField(User)
    pic = models.ForeignKey(ProfilePhoto, on_delete=models.SET_NULL, null=True, blank=True)

    #def __init__(self, *args, **kwargs):
    #    super(UserProfile, self).__init__(*args, **kwargs)
        #self.save()
        #self.pic = self.createDefaultPic()
        
    def save(self, *args, **kwargs):        
        print "HAHAHAHAH"
        
        super(UserProfile, self).save(*args, **kwargs)
        
        if not self.pic:
            self.pic = create_profile_photo(self.user)
        
    def __unicode__(self):
        return self.user.username


class Place(NameSlugModel):
    ''' A venue that serves food, e.g., restaurant, chain, cart, etc. '''
    derivedTypeName = models.CharField(max_length=30, blank=True)
    
    def get_display_name(self):
        return self.__dispatcher().get_display_name()
        
    def get_locations(self):
        return self.__dispatcher().get_locations()
    
    def __dispatcher(self):
        return getattr(self, str(self.derivedTypeName))
               
    def __unicode__(self):
        return self.__dispatcher().__unicode__()

class Chain(Place):
    ''' A collection of restaurants under the same name'''
    
    def get_display_name(self):
        return self.name + ' (multiple locations)'
    
    def get_locations(self):
        return [{"lat": restaurant.lat, "lon": restaurant.lon} for restaurant in self.restaurant_set.all()]
    
    def save(self, *args, **kwargs):
        self.derivedTypeName = 'chain'
        super(Chain, self).save(*args, **kwargs)
   
    def __unicode__(self):
        return self.name + ' (chain)'

class Restaurant(Place):
    address = models.CharField(max_length=140, blank=True)
    date_added = models.DateTimeField(auto_now_add=True, default=datetime.now())
    last_modified = models.DateTimeField(auto_now=True, default=datetime.now())
    chain = models.ForeignKey(Chain, null=True, blank=True)
    lat = models.FloatField()
    lon = models.FloatField()
    
    def get_display_name(self):
        return self.name + ' (restaurant)'
    
    def get_locations(self):
        return {"lat": self.lat, "lon": self.lon}
    
    def save(self, *args, **kwargs):
        self.derivedTypeName = 'restaurant'
        super(Restaurant, self).save(*args, **kwargs)
                    
    def __unicode__(self):
        return self.name + ' (' + str(self.address) + ')'
    
class Dish(NameSlugModel):
    desc = models.TextField(null=True, blank=True)
    photos = generic.GenericRelation(Photo, null=True, blank=True, related_name="wtf")
    pic = models.ForeignKey(Photo, on_delete=models.SET_NULL, null=True, blank=True)
    categories = models.ManyToManyField('Category', through='CategoryMembership', null=True, blank=True)
    users_voted_good = models.ManyToManyField(User, related_name='dish_set_good', null=True, blank=True)
    users_voted_bad = models.ManyToManyField(User, related_name='dish_set_bad', null=True, blank=True)
    place = models.ForeignKey(Place, null=True, blank=True)
    
    # used in serializer
    def get_locations(self):
        return self.place.get_locations()
    
    # used in serializer
    def get_display_image_src(self):
        return self.pic.display.url

    # used in serializer
    def get_thumbnail_image_src(self):
        return self.pic.thumbnail.url
    
    
        
    def save(self, *args, **kwargs):        
        print "Entering save method of Dish model..."
        
        super(Dish, self).save(*args, **kwargs)
        
        if not self.pic:
            self.pic = create_dish_photo(self)

    def __unicode__(self):
        return self.name + ' at ' + str(self.place)

class Category(NameSlugModel):
    dishes = models.ManyToManyField(Dish, through='CategoryMembership')
    ranking = models.CommaSeparatedIntegerField(max_length=100, default="", blank=True)

    def __unicode__(self):
        return self.name

class CategoryMembership(models.Model):
    dish = models.ForeignKey(Dish)
    category = models.ForeignKey(Category)
    
    def __unicode__(self):
        return str(self.dish) + " in " + str(self.category)
    
class Review(models.Model):
    dish = models.ForeignKey(Dish)
    profile = models.ForeignKey(UserProfile)
    text = models.TextField()
    date_added = models.DateField(auto_now_add=True)
    last_modified = models.DateField(auto_now=True)
    slug = models.SlugField(unique=True, editable=False)
    rating = models.DecimalField(decimal_places=2, max_digits=5)

    def save(self, *args, **kwargs):
        """
        Based on the Tag save() method in django-taggit, this method simply
        stores a slugified version of the name, ensuring that the unique
        constraint is observed
        """

        self.slug = slug = slugify(' '.join((self.profile.user.username, self.dish.name, self.dish.place.name)))
        i = 0
        while True:
            try:
                savepoint = transaction.savepoint()
                res = super(Review, self).save(*args, **kwargs)
                transaction.savepoint_commit(savepoint)
                return res
            except IntegrityError:
                transaction.savepoint_rollback(savepoint)
                i += 1
                self.slug = '%s_%d' % (slug, i)